{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2bf68d14",
   "metadata": {},
   "source": [
    "python函数定义非常简单，但是灵活度非常大，除了必选参数，还可以使用默认参数，可变参数和关键字参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2eb3d7c8",
   "metadata": {},
   "source": [
    "### 位置参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ebd31659",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x,n):\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n -1\n",
    "        s = s * x\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "95e46ba6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(5,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5ea2f563",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3125"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(5,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b70b55d4",
   "metadata": {},
   "source": [
    "x,n这两个参数都是位置参数，调用函数时，传入的两个值一次赋给这两个参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1804f2ed",
   "metadata": {},
   "source": [
    "### 默认参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e9d68f77",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x,n = 2):\n",
    "    s = 1\n",
    "    while n > 0:\n",
    "        n = n -1\n",
    "        s = s * x\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "deab5ed6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(5)"
   ]
  },
  {
   "cell_type": "raw",
   "id": "d400f59f",
   "metadata": {},
   "source": [
    "默认参数可以简化函数的调用：\n",
    "1.必选参数在前，默认参数在后\n",
    "2.设置默认参数时，把变化大的参数放在前面，变化小的参数放在后面，变化小的参数可以作为默认参数\n",
    "有多个默认参数时，调用时，可以按顺序提供默认参数\n",
    "也可以不按顺序，但需要把参数名写上。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "709d8346",
   "metadata": {},
   "source": [
    "#### 默认函数的坑"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a13cfe0",
   "metadata": {},
   "source": [
    "定义默认参数要牢记一点：默认参数必须指向不变对象！"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c8ac2644",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_end(L = None):\n",
    "    if L is None:\n",
    "        L = []\n",
    "    L.append('END')\n",
    "    return L"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "3eda54f5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END']"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "d80f030a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['END']"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_end()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e176b2e",
   "metadata": {},
   "source": [
    "不变对象一旦创建，对象内部的数据就不能修改，这样就减少了由于修改数据导致的错误。多任务环境下同时读取对象不需要加锁。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0469f38",
   "metadata": {},
   "source": [
    "### 可变参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef5adfa0",
   "metadata": {},
   "source": [
    "在Python函数中，还可以定义可变参数。也就是传入参数的个数时可变的"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30588414",
   "metadata": {},
   "source": [
    "定义可变参数和定义一个list或tuple参数相比，仅仅在参数前面加了一个*号，在函数内部，参数numbers接收到的是一个tuple"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "72989aa2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def calc(*numbers):\n",
    "    sum = 0\n",
    "    for n in numbers:\n",
    "        sum = sum + n * n\n",
    "    return sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "f19fbe6f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "calc(1,2,3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2507b38f",
   "metadata": {},
   "source": [
    "如果已有一个list或者tuple，要调用一个可变参数，可以如下操作："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "bfb92295",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "nums = [1,2,3,4]\n",
    "calc(*nums)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75615989",
   "metadata": {},
   "source": [
    "*nums表示把nums这个list的所有元素作为可变参数传进去"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f30a61b",
   "metadata": {},
   "source": [
    "### 关键字参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3d00d86f",
   "metadata": {},
   "source": [
    "可变参数允许你传入0个或任意个参数，这些可变参数在函数调用时自动组装成为一个tuple。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a818bd1e",
   "metadata": {},
   "source": [
    "关键字参数允许你传入0个或任意个含参数名的参数，这些关键字参数在函数内部自动组装为一个dict"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "88171f0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def pearson(name, age, **kw):\n",
    "    print('name:', name, 'age:', age, 'other:', kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b52f40b",
   "metadata": {},
   "source": [
    "函数person接受关键字参数kw"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "df2c71f7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Michael age: 30 other: {}\n"
     ]
    }
   ],
   "source": [
    "pearson('Michael', 30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "5500ff12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Bob age: 35 other: {'city': 'Beijing'}\n"
     ]
    }
   ],
   "source": [
    "pearson('Bob', 35, city='Beijing')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "3fd4b942",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer', 'city': 'Shanghai'}\n"
     ]
    }
   ],
   "source": [
    "pearson('Adam', 45, gender='M', job='Engineer', city='Shanghai')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43ab48ec",
   "metadata": {},
   "source": [
    "关键字参数可以扩展函数的功能，如果调用这愿意提供更多的参数，我们也能接收到。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "551ac67f",
   "metadata": {},
   "source": [
    "和可变参数类似，也可以先组装一个dict，然后把该dict转换为关键字参数传进去。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f2b05d89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name: jack age: 24 other: {'city': 'Beijing', 'job': 'Teacher'}\n"
     ]
    }
   ],
   "source": [
    "extra = {'city': 'Beijing', 'job': 'Teacher'}\n",
    "pearson('jack', 24, **extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "feedee2e",
   "metadata": {},
   "source": [
    "extra表示把extra这个dict的所有key-value用关键字参数传入到函数的kw参数，kw将获得一个dict，注意kw获得dict是extra的一份拷贝，对kw的\n",
    "改动不会影响到函数外的extra"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b73621b6",
   "metadata": {},
   "source": [
    "### 命名关键字参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3290e4c",
   "metadata": {},
   "source": [
    "如果要限制关键字参数的名字，就可以用命名关键字参数，例如，只接收city和job作为关键字参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "1877b103",
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, *, city, job):\n",
    "    print(name, age, city, job)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6758ee2",
   "metadata": {},
   "source": [
    "星号后面的参数视为命名关键字参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "d16a8764",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack 24 Beijing Teacher\n"
     ]
    }
   ],
   "source": [
    "person('jack', 24, **extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d120dca8",
   "metadata": {},
   "source": [
    "如果函数定义中已经有了一个可变参数，后米娜跟着的命名关键字参数就不再需要一个特殊分隔分号"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "30a548e7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack 24 (1, 2, 3, 'nihao') Beijing Teacher\n"
     ]
    }
   ],
   "source": [
    "def person(name , age , *args, city, job):\n",
    "    print(name, age, args, city, job)\n",
    "L = [1,2,3,'nihao']\n",
    "person('jack', 24, *L, **extra)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff15cea1",
   "metadata": {},
   "source": [
    "命名关键字参数必须传入参数名，这和位置参数不同，没有传入参数名的话，调用会出错"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e11081a",
   "metadata": {},
   "source": [
    "命名关键字参数可以有缺省值，从而简化调用："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "fd1cd53c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def person(name, age, *, city='Beijing', job):\n",
    "    print(name, age, city, job)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "52b3c5ee",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "jack 24 Beijing player\n"
     ]
    }
   ],
   "source": [
    "person('jack', 24, job='player')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1a8af53",
   "metadata": {},
   "source": [
    "使用命名关键字参数时，要特别注意，如果没有可变参数，就必须加一个星号作为特殊分隔符，否则\n",
    "python解释器无法识别位置参数和命名关键字参数"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30e21dd8",
   "metadata": {},
   "source": [
    "### 参数组合"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15824f1b",
   "metadata": {},
   "source": [
    "python定义函数，可以用必选参数，默认参数，可变参数，关键字参数和命名关键字参数组合使用。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6da839d9",
   "metadata": {},
   "source": [
    "参数定义的顺序必须是：必选参数，默认参数，可变参数，命名关键字参数，关键字参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "0eddd090",
   "metadata": {},
   "outputs": [],
   "source": [
    "def f1(a,b,c=0,*args, **kw): #必选参数，默认参数，可变参数，关键字参数\n",
    "    print('a =',a,'b =',b,'c =', c, 'args =',args, 'kw =', kw)\n",
    "def f2(a,b,c=0, *, d, **kw): #必选参数，默认参数，命名关键字参数，关键字参数\n",
    "    print('a =',a,'b =',b,'c =', c, 'd =',d, 'kw =', kw)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "729b0b50",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 0 args = () kw = {}\n"
     ]
    }
   ],
   "source": [
    "f1(1,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "979a6224",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = () kw = {}\n"
     ]
    }
   ],
   "source": [
    "f1(1,2,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "5b3019c5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}\n"
     ]
    }
   ],
   "source": [
    "f1(1,2,3,'a','b')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "668e0f42",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = ('a',) kw = {'x': 'heihei'}\n"
     ]
    }
   ],
   "source": [
    "f1(1,2,3,'a',x='heihei')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "522dd381",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 1 b = 2 c = 3 args = (4,) kw = {'d': 99, 'x': '#'}\n"
     ]
    }
   ],
   "source": [
    "ags = (1,2,3,4)\n",
    "kw = {'d':99, 'x':'#'}\n",
    "f1(*ags, **kw)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "12362fa8",
   "metadata": {},
   "source": [
    "对于任意函数，都可以通过类似func(*args, **kw)的形式调用它，无论它的参数定义是如何定义的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2141730e",
   "metadata": {},
   "source": [
    "注意，虽然可以组合多大5种啊承诺书，但不要同时使用太多的组合，否则函数接口的可理解性很差。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7cba790d",
   "metadata": {},
   "source": [
    "### 小结"
   ]
  },
  {
   "cell_type": "raw",
   "id": "c3e9ad92",
   "metadata": {},
   "source": [
    "1.默认参数一定要用不可变对象，否则会有逻辑错误。\n",
    "2.可变参数，args接收的是一个tuple\n",
    "3.关键字参数，kw接收的是一个dict\n",
    "4.命名关键字参数是为了限制点用着可以传入的参数名，同时可以提供默认值\n",
    "5.定义命名关键字参数不要王了*号分隔符号。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f43fd5be",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
